package  brwarner.game.states
{
	import brwarner.game.GameGrid;
	import brwarner.game.God;
	import brwarner.game.GridSquare;
	import brwarner.game.Humans;
	import brwarner.game.ui.AttackButton;
	import brwarner.game.ui.AttackPanel;
	import brwarner.game.ui.AttackPanelEvent;
	import brwarner.game.ui.ManaPanel;
	import brwarner.game.ui.HumanPanel;
	import brwarner.game.GameState;
	import brwarner.game.Game;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import brwarner.game.Attacks;
	import flash.ui.Keyboard;
	import ivan.*;
	import brwarner.GridEvent;
	import brwarner.game.GameConstants;
	
	/**
	 * ...
	 * @author Brook Jensen
	 */
	public class GridState extends GameState 
	{
		//Mana panel
		private var _manaPanel:ManaPanel;
		
		//Humans panel
		private var _humanPanel:HumanPanel;
		
		//God
		private var _god:God;
		
		//Human team
		private var _humans:Humans;
		
		//Game grid
		private var _grid:Grid;
		
		//Power menu
		private var _powers:AttackPanel;
		
		//Attack items
		private var _attackItems:Array;
		
		//Game grid
		private var _gameGrid:GameGrid;
		
		//Precalculated list of infected squares
		private var _infected:Array;
		
		//Selected direction (UP,RIGHT,DOWN,LEFT)
		private var _direction:Number = 0;
		
		public function GridState( g:Game ) 
		{
			//Setup the state
			super(g);
			bgcolor = 0x000000;
			
			//Create god
			_god = new God( GameConstants.INITIAL_MANA[0], GameConstants.INITIAL_MANA[1], GameConstants.INITIAL_MANA[2], GameConstants.INITIAL_MANA[3] );
			_manaPanel = new ManaPanel( _god );
			addChild( _manaPanel );
			
			_infected = new Array();
			_humans = new Humans();
			
			//Create humans panel
			_humanPanel = new HumanPanel(_humans);
			_humanPanel.y = _manaPanel.y + _manaPanel.height + 10;
			addChild(_humanPanel);
			
			//Create "the grid"
			_grid = new Grid(GameConstants.GRID_SIZE);
			_grid.y = 30;
			_grid.x = _manaPanel.x + _manaPanel.width + 15;
			addChild(_grid);
			
			//Create the game grid
			_gameGrid = new GameGrid(GameConstants.GRID_SIZE);
			
			//Register for events
			_grid.addEventListener(GridEvent.GRID_CLICK, gridClick );
			_grid.addEventListener(GridEvent.GRID_NEED_ALPHA, showAttack );
			game.parent.parent.addEventListener(KeyboardEvent.KEY_DOWN, keyPress );
			
			//Create our menu
			_powers = new AttackPanel();
			_powers.y = _humanPanel.y + _humanPanel.height + 5;
			addChild(_powers);
			
			//Create all the attack menu items
			_attackItems = new Array();
			for ( var i = 0; i < Attacks.ATTACKS.length; i++ )
			{
				var item:AttackButton = new AttackButton( Attacks.ATTACKS[i].image, i );
				_attackItems.push(item);
				_powers.addItem( item );
			}
			
			//Hook to attack selection event
			_powers.addEventListener(AttackPanelEvent.ATTACK_SELECTED, onPowerChanged);
			
			refreshDisplay();
			
			//Hook into the game grid's status event
			_gameGrid.addEventListener(GridEvent.GRID_STATUS_CHANGED, onGridStatus );
			
			//Create first base
			_gameGrid.getTile( new Point(0, 0) ).Type = GridSquare.PLAIN;
			_gameGrid.getTile( new Point(0, 0) ).Infection = GridSquare.HUMAN_BASE;
			
			//Create the tower
			_gameGrid.getTile( new Point( GameConstants.GRID_SIZE-1, GameConstants.GRID_SIZE-1 ) ).Type = GridSquare.TOWER;
		}
		
		private function onPowerChanged( e:AttackPanelEvent )
		{
			_grid.ArrowVisible = Attacks.ATTACKS[e.ID].directional;
		}
		
		private function set Direction( d:Number ):void {
			_direction = d;
			_grid.ArrowDirection = d;
		}
		private function get Direction( ):Number { return _direction; }
		
		private function keyPress( e:KeyboardEvent ) {
			//Handle direction
			if ( e.charCode == 'a'.charCodeAt(0) ) Direction -= 2;
			else if ( e.charCode == 'd'.charCodeAt(0) ) Direction += 2;
			if ( _direction == -2 ) Direction = 6;
			if ( _direction == 8 ) Direction = 0;
			
			//Handle attack quickmenu
			if ( '0'.charCodeAt(0) <= e.charCode && e.charCode <= '9'.charCodeAt(0) ) {
				var attack:int = 0;
				if (e.charCode == '0'.charCodeAt(0)) attack = 9;
				else attack = e.charCode - '1'.charCodeAt(0);
				_powers.Active = attack;
			}
		}
		
		//Checks if the attack is valid on a certain type
		private function attackValid( id:Number, tile:GridSquare ):Boolean
		{
			if ( _gameGrid.GameOver ) return false;
			if ( Attacks.ATTACKS[id].type == true ) return true;
			if ( tile.Infected ) return false;
			for ( var i = 0; i < Attacks.ATTACKS[id].type.length; i++ ) {
				if ( Attacks.ATTACKS[id].type[i] == tile.Type ) return true;
			}
			return false;
		}
		
		private function showAttack( e:GridEvent ):void
		{
			//Just that tile for no attack selected
			if ( _powers.Active == -1 ) {
				_grid.setOpacityGrid( [ e.Tile ], true );
				return;
			}
			if ( !attackValid(_powers.Active, _gameGrid.getTile( e.Tile ) ) ) {
				_grid.setOpacityGrid( [ e.Tile ], true );
				return;
			}
			
			//Get the burst
			if( Attacks.ATTACKS[_powers.Active].directional )
				_grid.setOpacityGrid( Attacks.ATTACKS[_powers.Active].areaFunc( e.Tile, _gameGrid, _direction ) );
			else
				_grid.setOpacityGrid( Attacks.ATTACKS[_powers.Active].areaFunc( e.Tile, _gameGrid ) );
		}
		
		private function onGridStatus( e:GridEvent ):void
		{
			//Refresh that particular tile
			_grid.setTile( e.Tile, _gameGrid.getTile(e.Tile).SpriteType );
			_grid.setInfected( e.Tile, _gameGrid.getTile(e.Tile).Infected );
			
			//Keep track of infected squares (check if its infected and not in list
			if ( _gameGrid.getTile(e.Tile).Infected && _infected.every(
					function( p:Point, i:Number, a:Array ) : Boolean { return !e.Tile.equals( p ); } 
				) )
				{ _infected.push( e.Tile ); }
				
			if ( !_gameGrid.getTile( e.Tile ).Infected )
			{
				//Check if its in the array already
				for ( var i = 0; i < _infected.length; i++ )
				{
					if ( e.Tile.equals( _infected[i] ) ) {
						_infected.splice( i, 1 );
						break;
					}
				}
			}
		}
		
		//Refreshes the display of the grid
		private function refreshDisplay( )
		{
			for ( var a = 0; a < GameConstants.GRID_SIZE; a++ )
			{
				for( var b = 0; b < GameConstants.GRID_SIZE; b++ )
				{
					//Get square
					var type:Number = _gameGrid.getTile( new Point( b, a ) ).SpriteType;
					_grid.setTile( new Point( b, a ), type );
				}
			}
		}
		
		private function gridClick( e:GridEvent ):void
		{
			while ( _gameGrid.Block ) ;
			
			//No attack selected
			if ( _powers.Active == -1 ) return;
			
			//Get selected attack
			var attack:Object = Attacks.ATTACKS[ _powers.Active ];
			
			if ( !attackValid(_powers.Active, _gameGrid.getTile( e.Tile )) ) return;
			
			//Check mana
			if ( _god.Fire >= attack.fire && _god.Water >= attack.water && 
				_god.Ground >= attack.ground && _god.Air >= attack.air )
			{
				//Use mana
				_god.Fire -= attack.fire;
				_god.Water -= attack.water;
				_god.Ground -= attack.ground;
				_god.Air -= attack.air;
			
				//Run corresponding attack
				if( !attack.directional )
					attack.damageFunc( e.Tile, _gameGrid );
				else
					attack.damageFunc( e.Tile, _gameGrid, Direction );
			}
		}
		
		private var _counter = 0;
		
		override public function update( ) : void
		{
			if ( _gameGrid.GameOver ) return;
			if ( _counter % GameConstants.FRAMES_PER_TURN == 0 && GameConstants.HUMANS_ACTIVE)
			{
				//Run the human turn
				_humans.think( _infected.slice(), _gameGrid );
			}
			if ( _counter % GameConstants.FRAMES_PER_GOD_TURN == 0 && _counter != 0)
			{
				//Replenish mana
				for ( var a = 0; a < GameConstants.GRID_SIZE; a++ ) {
					for ( var b = 0; b < GameConstants.GRID_SIZE; b++ ) {
						//Check that square
						var tile:GridSquare = _gameGrid.getTile( new Point( a, b ) );
						
						if ( !tile.Infected && tile.Type < GridSquare.RANDOM_MAX ) {
							//Generate mana
							var mana:Array = GameConstants.GENERATION_RATES[tile.Type];
							_god.Fire = Math.min( _god.Fire + mana[0] * GameConstants.MANA_GEN_MULT, GameConstants.MAX_MANA[0] );
							_god.Water = Math.min( _god.Water + mana[1] * GameConstants.MANA_GEN_MULT, GameConstants.MAX_MANA[1] );
							_god.Air = Math.min( _god.Air + mana[2] * GameConstants.MANA_GEN_MULT, GameConstants.MAX_MANA[2] );
							_god.Ground = Math.min( _god.Ground + mana[3] * GameConstants.MANA_GEN_MULT, GameConstants.MAX_MANA[3] );
						}
					}
				}
			}
			_counter++;
			_gameGrid.update();
		}
		
	}

}